﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Director.DomainModels;
using Director.Indicators.Basic;
using Color = System.Windows.Media.Color;
using FontFamily = System.Windows.Media.FontFamily;
using Image = System.Windows.Controls.Image;
using Point = System.Windows.Point;

namespace Director.ViewModels
{
	class LeaderStrategyVM : AbstractStrategyVM
	{
		const int ДенегНаОднуПозицию = 15000;

		//public int ДенегНаОднуПозицию
		//{
		//	get { return (int)(_real ? 15000 : (100000 + Portfolio.PnL)/6.25m); }
		//}

		readonly Dictionary<Security, decimal> _yesterdayClose = new Dictionary<Security, decimal>();
		readonly Dictionary<Security, decimal> _prevClose = new Dictionary<Security, decimal>();
		private DateTime _date = DateTime.MinValue;
		private bool _run;
		private Security _lastLeader;
		private bool _real = false;

		public Security LastLeader
		{
			get
			{
				return _lastLeader;
			}
			private set
			{
				_lastLeader = value;
				if (_real)
				{
					DataManager.Instance.DirectorStorage.SetLeader(value);
				}
			}
		}

		public LeaderStrategyVM(MainVM mainVM)
			: base(mainVM)
		{
			_bitmap = BitmapFactory.New(1000, 1000);
		}

		public override string Title
		{
			get { return "Leader"; }
		}

		protected override TimeFrame.ELength TimeFrameLength
		{
			get { return TimeFrame.ELength.M15; }
		}

		public override void Init()
		{
			var securities = DataManager.Instance.SharesByCodes.ToArray();
			Subscribe(securities);

			//PnL:     	 49 509,27
			//DrawDown:	 4 581,48
			//Unrealized:	 49 681,7
			//K=10.8
			_smaId = AddIndicator(securities, () => new StrategySimpleMovingAverage(c => c.Close, 50));

			//PnL:     	 51 044,38
			//DrawDown:	 6 649,10
			//Unrealized:	 51 319,18
			//K=7.6
			//_smaId = AddIndicator(securities, () => new StrategySimpleMovingAverage(c => c.Close, 100));

			//_smaId = AddIndicator(securities, () => new StrategySimpleMovingAverage(c => c.Close, 20));

			base.Init();
		}

		protected override bool CanWork
		{
			get { return true; }
		}

		protected override void СтартВБоевомРежиме()
		{
			_real = true;
			var lastLeaderCode = DataManager.Instance.DirectorStorage.GetLeader();
			_lastLeader = lastLeaderCode == null ? null : DataManager.Instance.Securities.Values.First(e => e.Code == lastLeaderCode);
			if (_lastLeader!=null && GetPosition(_lastLeader) == 0)
			{
				LastLeader = null;
			}
		}

		protected override void CandlesTimeElapsed(SecCandle[] candles, bool forcePrevDayData)
		{
			var dateTime = candles.Max(e => e.Candle.Date);

			if (_date == DateTime.MinValue)
			{
				_date = dateTime;
			}

			if (forcePrevDayData)
			{
				foreach (var secCandle in candles)
				{
					Last[secCandle.Security] = secCandle.Candle;
				}
				foreach (var s in Last.Keys)
				{
					_yesterdayClose[s] = Last[s].Close;
				}
				//_run = true;
				_date = Last.Values.Max(e=>e.Date.Date);
				return;
			}
			else if (_date != dateTime.Date)
			{
				//на следующий день

				Debug.WriteLine("---------------------- " + _date.ToLongDateString());

				if (!_real)
				{
					foreach (var s in Last.Keys)
					{
						_yesterdayClose[s] = Last[s].Close;
					}
				}
				else
				{
					_run = true;
				}

				if (!_real || dateTime.TimeOfDay == new TimeSpan(0, 10, 0, 0))
				{
					foreach (var pair in candles)
					{
						var s = pair.Security;
						var candle = pair.Candle;

						var pos = GetPosition(s);
						if (pos == 0) continue;

						if (candle.Close < GetStopLoss(s))
						{
							//Если гэп начала дня пробил стоп
							Sell(s, pos, Last[s].Close);
						}
					}
				}
				
				_date = dateTime.Date;
				if (!_run)
				{
					_run = true;
					return;
				}
			}

			foreach (var pair in candles)
			{
				if (Last.ContainsKey(pair.Security))
				{
					_prevClose[pair.Security] = Last[pair.Security].Close;
				}
				Last[pair.Security] = pair.Candle;
			}

			foreach (var security in candles.Select(s=>s.Security))
			{
				var v = GetIndicatorValue(security, _smaId, 0);
				if (v != null)
				{
					if (GetPosition(security) > 0)
					{
						if (_prevClose[security] > v && Last[security].Close < v)
						{
							Sell(security, GetPosition(security), Last[security].Close);
						}
						else if (Last[security].Open > v && Last[security].Close< v)
						{
							Sell(security, GetPosition(security), Last[security].Close);
						}
					}
				}
			}


			if (!_run || dateTime.Hour >= 18 || dateTime.Hour <= 12 || _yesterdayClose.Count <= 0 || !IsToday(dateTime)) return;
			
			//ищем лидера превосходящего окружающих на 1.5%

			//PnL:			 76 441,09
			//ПросадкаОткрытых:	 1 041,50
			//Unrealized:		 76 651,09
			//ПросадкаСчета:		 6 599,39
			//K:			 11,6
			//BS count:		388/387
			//Max in work		101 140,48
			//15 000

			var leader = Last.ToDictionary(p => p.Key, p => p.Value.Close / _yesterdayClose[p.Key]).OrderByDescending(p => p.Value).FirstOrDefault(e => e.Value > 101.5m.Percents());
			
			//если лидер обнаружен и мы еще не в нем
			if (leader.Key != null && leader.Key != LastLeader)
			{
				Debug.WriteLine("LDR: " + LastLeader + " -> " + leader.Key);
				LastLeader = leader.Key;

				var ценаЛота = (Last[LastLeader].Close * leader.Key.Lotsize);

				var myLot = (int)(ДенегНаОднуПозицию / ценаЛота);

				var pos = GetPosition(leader.Key);

				if (pos == 0)
				{
					var v = GetIndicatorValue(LastLeader, _smaId, 0);
					if (v==null || v < Last[LastLeader].Close)
					{
						Buy(LastLeader, myLot, Last[LastLeader].Close);
						SetStopLoss(LastLeader, Last[LastLeader].Close);
					}
				}
				else
				{
					//Если произошла смена лидера - сдвиг трейлинг стопа
					SetStopLoss(LastLeader, Last[LastLeader].Close);
				}
			}

			foreach (var s in Last.Keys)
			{
				var pos = GetPosition(s);
				if (pos != 0)
				{
					//Если позиция просела на 0,5% ниже средней - закрываем
					//НО Если эта позиция по лидирующей бумаге - пусть живет
					if (GetAveragePrice(s) / Last[s].Close > 105.Percents() && leader.Key != s)
					{
						Sell(s, pos, Last[s].Close);
						pos = 0;
					}
				}
			}
		}

		public override EDocId DocId
		{
			get { return EDocId.LEADER; }
		}

		public override string StrategyId
		{
			get { return "LEADER"; }
		}

		#region drawing

		private WriteableBitmap _bitmap;
		private Image _image;
		static readonly Typeface _typeface = new Typeface(new FontFamily("Consolas"), FontStyles.Normal, FontWeights.Normal, new FontStretch());
		private Guid _smaId;

		public Image Image
		{
			get { return _image; }
			set
			{
				_image = value;
				_image.SizeChanged += (sender, args) => Draw();
			}
		}

		public ImageSource ImageSource
		{
			get { return _bitmap; }
		}

		protected override void Draw()
		{
			if (!_real) return;
			if (Image == null) return;
			if (_yesterdayClose.Count == 0 || Last.Count == 0) return;


			var w = Image.ActualWidth;
			var h = Image.ActualHeight;

			_bitmap = DrawInner(_bitmap, w, h);
			OnPropertyChanged(() => ImageSource);
		}

		private WriteableBitmap DrawInner(WriteableBitmap bitmap, double w, double h)
		{
			if (Math.Abs(bitmap.Width - (int)w) > double.Epsilon || Math.Abs(bitmap.Height - (int)h) > double.Epsilon)
			{
				bitmap = bitmap.Resize((int)w, (int)h, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
			}

			var cx = w / 2;
			var cy = h / 2;

			var r = (int)Math.Min(w, h) / 4;

			bitmap.Lock();
			bitmap.Clear();

			var secs = Last.Keys.ToArray();

			var top = secs.ToDictionary(security => security, security => Last[security].Close / _yesterdayClose[security]);
			var points = secs.ToDictionary(s => s, s => new Point());

			var topxy = new List<int>();

			var fi = Math.PI * 2 / secs.Length;
			var ro = fi / 2;

			for (int i = 0; i < secs.Length; i++)
			{
				var sec = secs[i];

				var x = r * Math.Cos(ro);
				var y = r * Math.Sin(ro);

				points[sec] = new Point((int)x, (int)y);

				var topx = cx + x * (1 + ((double)top[sec] - 1) * 2);
				var topy = cy + y * (1 + ((double)top[sec] - 1) * 2);

				topxy.Add((int)topx);
				topxy.Add((int)topy);

				ro += fi;
			}

			topxy.Add(topxy[0]);
			topxy.Add(topxy[1]);

			bitmap.FillPolygon(topxy.ToArray(), Color.FromArgb(16, 100, 0, 0));

			for (int i = 10; i > 1; --i)
			{
				var ir = r * i / 5;
				bitmap.DrawEllipseCentered((int)cx, (int)cy, ir, ir, Colors.LightGray);
				Charter.DrawText(bitmap, new Point((int)cx + 1, (int)cy - ir - 5), (i - 5) * 10 + "%", 10, Brushes.Gray, TextAlignment.Center);
			}

			foreach (var pair in points)
			{
				var security = pair.Key;

				var to = new Point(cx + pair.Value.X * 1.3, cy + pair.Value.Y * 1.3);
				bitmap.DrawLineAa((int)cx, (int)cy, (int)to.X, (int)to.Y, security == LastLeader ? Colors.Goldenrod : Colors.LightGray);

				Charter.DrawText(bitmap, to, security.Code, 10, Brushes.Gray, TextAlignment.Center);

				var pos = GetPosition(security);
				if (pos != 0)
				{
					var avPrice = GetAveragePrice(security);
					var curPrice = Last[security].Close;

					var d = (double)(curPrice / avPrice - 1m) * 2 + 1;

					var dPos = new Point(cx + pair.Value.X * d, cy + pair.Value.Y * d);

					var color = d > 1 ? Colors.GreenYellow : Colors.LightCoral;

					bitmap.FillEllipseCentered((int)dPos.X, (int)dPos.Y, 4, 4, color);
					bitmap.DrawEllipseCentered((int)dPos.X, (int)dPos.Y, 4, 4, Colors.Gray);

					var stopLoss = GetStopLoss(security);

					if (stopLoss > curPrice)
					{

					}

					if (stopLoss != 0)
					{
						var sd = (double)(stopLoss / avPrice - 1m) * 2 + 1;
						var sdPos = new Point(cx + pair.Value.X * sd, cy + pair.Value.Y * sd);
						bitmap.FillEllipseCentered((int)sdPos.X, (int)sdPos.Y, 2, 2, stopLoss > avPrice ? Colors.Green : Colors.Red);
					}

					var v = GetIndicatorValue(security, _smaId, 0);
					if (v != null)
					{
						var sd = (double)(v / avPrice - 1m) * 2 + 1;
						var sdPos = new Point(cx + pair.Value.X * sd, cy + pair.Value.Y * sd);
						bitmap.FillEllipseCentered((int)sdPos.X, (int)sdPos.Y, 2, 2, Colors.Brown);
					}
				}
			}

			bitmap.Unlock();

			return bitmap;
		}

		#endregion

	}

	public static class DecimalHelper
	{
		public static decimal Percents(this decimal d)
		{
			return d / 100m;
		}

		public static decimal Percents(this int d)
		{
			return d / 100m;
		}
	}
}